home *** CD-ROM | disk | FTP | other *** search
- Subject: v19i102: Save files that have changed in the last little while
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: Rayan Zachariassen <rayan@ai.toronto.edu>
- Posting-number: Volume 19, Issue 102
- Archive-name: backup
-
- The backup program is an online disk-to-disk incremental backup utility
- that maintains a file queue in a seperate partition, copying files that
- have been modified since its last run to the special backup filesystem.
- As space is needed on the backup partition, just enough of the oldest files
- are removed to make space for new file copies. In other words, a fixed
- size (in disk blocks) queue of recently modified files.
-
- Assumes SunOS filesystem interface (vnodes, etc.), would not be hard to
- port to straight BSD filesystem.
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # If this archive is complete, you will see the following message at the end:
- # "End of shell archive."
- #
- # Contents:
- # README Makefile backup.8 backup.c backup.conf
- # backup.filter getback.1 getback.c getback.sh ilw.c
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f README -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"README\"
- else
- echo shar: Extracting \"README\" \(1083 characters\)
- sed "s/^X//" >README <<'END_OF_README'
- XThe backup program is an online disk-to-disk incremental backup utility
- Xthat maintains a file queue in a seperate partition, copying files that
- Xhave been modified since its last run to the special backup filesystem.
- XAs space is needed on the backup partition, just enough of the oldest files
- Xare removed to make space for new file copies. In other words, a fixed
- Xsize (in disk blocks) queue of recently modified files.
- X
- X
- XDefaults, overridden by command line arguments:
- X
- X BACKUP /backup
- X CONFIG /etc/backup.conf
- X MAXSIZE 100k
- X DELTA 24hr
- X
- XAssumes SunOS filesystem interface (vnodes, etc.), would not
- Xbe hard to port to straight BSD filesystem.
- X
- XInstallation:
- X
- X- Edit Makefile.
- X- Tweak default parameters in backup.c if you don't like the above values.
- X- Create and install a configuration file, see the example in ./backup.conf.
- X- make; make install
- X- Allocate a /backup partition, create and mount an empty filesystem on it.
- X- Add a crontab entry, e.g.:
- X 37 4,12,20 * * * /local/etc/backup
- X
- XThanks to Ken Lalonde for code tweaks, getback.sh, and most of this README file
- X
- Xrayan
- END_OF_README
- if test 1083 -ne `wc -c <README`; then
- echo shar: \"README\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f Makefile -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"Makefile\"
- else
- echo shar: Extracting \"Makefile\" \(549 characters\)
- sed "s/^X//" >Makefile <<'END_OF_Makefile'
- XDESTDIR=/local
- XCOPTS = # -Bstatic
- XCFLAGS = $(COPTS) -DCONFIG=\"$(DESTDIR)/etc/backup.conf\"
- X
- Xall: backup getback
- X
- Xbackup: backup.o ilw.o
- X $(CC) $(CFLAGS) -o backup backup.o ilw.o
- X
- Xinstall: all
- X install -m 710 backup $(DESTDIR)/etc/backup
- X install -m 755 getback $(DESTDIR)/bin/getback
- X install -c -m 644 backup.8 $(DESTDIR)/man/man8
- X install -c -m 644 getback.1 $(DESTDIR)/man/man1
- X
- Xclean:
- X rm -f *.o backup getback make.log
- X
- Xdist:
- X shar README Makefile backup.8 backup.c backup.conf backup.filter getback.1 getback.c getback.sh ilw.c > backup.shar
- END_OF_Makefile
- if test 549 -ne `wc -c <Makefile`; then
- echo shar: \"Makefile\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f backup.8 -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"backup.8\"
- else
- echo shar: Extracting \"backup.8\" \(4871 characters\)
- sed "s/^X//" >backup.8 <<'END_OF_backup.8'
- X.TH BACKUP 8 "UofToronto"
- X.SH NAME
- Xbackup \- save files that have changed in the last little while
- X.SH SYNOPSIS
- X.B backup
- X[ -devz
- X.RI -b #
- X] [ -c
- X.I backup.conf
- X] [ -o
- X.I /backup
- X] [
- X.I /path
- X\&... ]
- X.SH DESCRIPTION
- X.I Backup
- Xcopies to a special filesystem hierarchy all files which have been modified
- Xsince the previous run of
- X.I backup
- Xon that subtree of the filesystem.
- XThe subtree(s) may be specified explicitly on the command line.
- XIf none are given, a configuration file (default is
- X.BR /local/etc/backup.conf )
- Xis read to determine which subtrees to scan for recently changed files.
- XIf subtrees are explicitly listed, a run is forced. Otherwise,
- X.I backup
- Xwill only do its deed if the subtree is due for backing up as indicated
- Xby the interval time, and the last backup scan time, in the configuration
- Xfile. If the subtree is not found in the configuration file, all files
- Xchanged during the last day will be selected.
- X.PP
- XThe format of the configuration file is:
- X.IP -
- Xlines starting with
- X.B #
- Xare ignored.
- X.IP -
- Xall other lines contain four space-separated fields:
- X.IP
- XField 1 is the path name of a subtree to back up.
- X.IP
- XField 2 is the desired interval between backups.
- X.IP
- XField 3 is the name of a filter file containing glob patterns,
- Xone per line, describing names of files to
- X.I ignore
- Xwhen scanning the subtree.
- X.IP
- XField 4 is a timestamp in the exact syntax of
- X.BR ctime (3).
- X.PP
- XThis is a sample configuration file:
- X.ta 1.7i,2.3i,3.7i
- X.nf
- X
- X# path intvl filter file last done
- X/usr 8h /dev/null Sat Apr 30 00:33:03 1988
- X/homes/neat/car 8h /etc/filter.u Sat Apr 30 00:33:03 1988
- X/homes/neat/cdr 8h /etc/filter.u Sat Apr 30 00:33:03 1988
- X/var 24h /dev/null Sat Apr 30 00:33:03 1988
- X.fi
- X.PP
- XThe first three fields are maintained manually (and defaulted if
- X.I backup
- Xis given a subtree argument not appearing in the configuration file).
- XThe last field is maintained by the program itself. After every successful
- Xrun, the configuration file is written out containing the updated timestamp
- Xinformation. The purpose of the configuration file is to allow all information
- Xbe maintained in this form, and the
- X.I backup
- Xprogram started very frequently by
- X.BR cron (8).
- X.PP
- XBacked up files are stored in a parallel directory hierarchy under the
- Xroot of the backup hierarchy (by default
- X.BR /backup ).
- XEach backed up file has associated with it a directory whose relative
- Xpathname under the root of the backup hierarchy is its full pathname.
- XThis directory contains one or more backed up files in files named by
- Xthe date of the backup. Thus, a user with a file
- X.br
- X.sp
- X.I /homes/neat/car/user/src/stuff.c
- X.br
- X.sp
- Xthat he has been working on and hence has been backed up a number
- Xof times might find a set of files
- X.br
- X.sp
- X.IR /backup/homes/neat/car/user/src/stuff.c/Apr30-06:05 ,
- X.IR /backup/homes/neat/car/user/src/stuff.c/Apr30-09:05 ,
- X.br
- X.sp
- Xand
- X.br
- X.sp
- X.IR /backup/homes/neat/car/user/src/stuff.c/Apr30-12:05 .
- X.br
- X.sp
- XPermissions and owner/group are copied with the files.
- X.PP
- XAs the backups are very expensive in terms of file space only
- Xfiles of less than some size, currently 100 kilobytes, are saved.
- XFiles can be deselected by creating a filter file, and adding to it
- Xglob patterns that would match suspicious names. Typically,
- Xfor example
- X.IR core ,
- X.IR a.out ,
- Xemacs temporaries like
- X.I #*
- Xor
- X.I *~
- Xand many others, are not backed up.
- X.PP
- XWhen filespace usage on the backup root's file system passes a high water mark,
- Xcurrently 95%,
- X.I
- Xbackup
- Xdeletes files in order by modification date until it has enough space under
- Xthe high water mark to add new backup files.
- X.PP
- X.I Backup
- Xreads the entire i-list for the filesystem, saving information such as the
- Xmodification time and type of the inode. Thereafter all the appropriate
- Xfile names are identified by tree walks in the filesystem, and backed up.
- XIf files must be deleted from the backup hierarchy,
- Xthen it does the same for the backup subtree (only deleting instead of
- Xbackup up!).
- X.PP
- XThe various options are:
- X.IP -b
- Xthe argument is the largest size in kilobytes of files to consider backing up.
- X.IP -c
- Xspecifies an alternate configuration file.
- X.IP -d
- Xturns a little bit of debugging output on.
- X.IP -e
- Xwill not ignore executable files when looking for files to back up.
- X.IP -o
- Xspecifies an alternate backup hierarchy to place backed up files.
- X.IP -v
- Xturns on verbosity, and can be doubled for more chatter.
- X.IP -z
- Xasks the program to go through the motions, but to not take any action
- Xwhatsoever.
- X.SH FILES
- X.ta 2.5i
- X/backup - mounted filesystem for backups
- X.br
- X/local/etc/backup.conf - backup configuration information
- X.SH INFO
- XOriginal idea by Ciaran O'Donnell of U of Waterloo, whose version was
- Xwritten in 1975. This is an independent reimplementation native to the
- XSunOS environment, written by Rayan Zachariassen at U of Toronto
- X(bug fixes and improvements to <rayan@ai.toronto.edu>).
- END_OF_backup.8
- if test 4871 -ne `wc -c <backup.8`; then
- echo shar: \"backup.8\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f backup.c -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"backup.c\"
- else
- echo shar: Extracting \"backup.c\" \(28044 characters\)
- sed "s/^X//" >backup.c <<'END_OF_backup.c'
- X/*
- X * backup - do incremental disk-to-disk backups of individual files
- X *
- X * This code is a complete reimplementation (for the SunOS environment)
- X * of an original idea by Ciaran O'Donnell. This implementation is
- X * Copyright 1988 by Rayan Zachariassen, solely to prevent you selling
- X * it or putting your name on it. Free (gratis) redistribution is
- X * encouraged.
- X */
- X
- X#ifndef lint
- Xstatic char *RCSid = "$Header: /ai/car/src/etc/backup/RCS/backup.c,v 1.2 89/05/23 22:13:19 rayan Exp $";
- X#endif lint
- X
- X#include <stdio.h>
- X#include <ctype.h>
- X#include <mntent.h>
- X#include <values.h>
- X#include <errno.h>
- X#include <sys/param.h>
- X#include <sys/types.h>
- X#include <sys/file.h>
- X#include <sys/stat.h>
- X#include <sys/time.h>
- X#include <sys/vnode.h>
- X#include <sys/vfs.h>
- X#include <ufs/inode.h>
- X#include <ufs/fs.h>
- X#include <sys/dir.h>
- X
- X#define HIWATER 95 /* % of backup filesystem used at high water */
- X#define LOWATER 85 /* % of backup filesystem used at low water */
- X
- X#define BACKUP "/backup" /* backup filesystem mount point */
- X#ifndef CONFIG
- X#define CONFIG "/etc/backup.conf" /* configuration file */
- X#endif /* !CONFIG */
- X
- X#define MAXSIZE 100000 /* default maximum size of files to back up */
- X
- X#define DELTA (24*60*60) /* default time since last backup */
- X
- Xint maxsize = MAXSIZE; /* maximum size in bytes of files to back up */
- Xint doexecs = 0; /* should we back up executables? */
- X
- Xstruct a_path {
- X char *path; /* path to tree hierarchy we want to back up */
- X char *glob; /* pointer to the contents of the filter file */
- X time_t newer; /* back up files newer than this time */
- X struct config *config;/* back pointer to config structure */
- X struct a_path *next;
- X};
- X
- Xstruct a_dev {
- X char *text; /* raw device name */
- X struct a_path *paths; /* pointer to list of paths */
- X struct a_dev *next; /* pointer to next device */
- X};
- X
- Xstruct a_dev *devlist = NULL;
- X
- Xstruct config {
- X char *path; /* top of hierarchy we want to back up */
- X char *interval; /* how often to do incrementals */
- X char *filter; /* name of file containing regexp's */
- X char *line; /* the config file line excluding time field */
- X time_t lasttime; /* last time incrementals were done here */
- X struct config *next;
- X};
- X
- Xchar *progname, *backup, *devbackup, *backupmnt;
- Xint debug, verbose, dryrun;
- Xtime_t now, lasttime;
- X
- Xextern int optind;
- Xextern char *optarg;
- Xextern char *emalloc();
- X
- X#define EMSG(x) (errno < sys_nerr ? sys_errlist[x] : \
- X (sprintf(errmsgbuf, "unknown error %d", errno), errmsgbuf))
- X
- Xextern int errno, sys_nerr;
- Xextern char *sys_errlist[];
- Xchar errmsgbuf[30];
- X
- Xmain(argc, argv)
- X int argc;
- X char *argv[];
- X{
- X int c, errflag;
- X char *cffile;
- X struct a_dev *lp;
- X struct a_path *pp;
- X struct config *cf, *cfp;
- X FILE *cf_fp;
- X extern time_t time();
- X extern char *getdevice(), *getpath(), *ctime();
- X extern struct config *readconfig();
- X extern void writeconfig();
- X
- X progname = argv[0];
- X errflag = 0;
- X dryrun = 0;
- X debug = verbose = 0;
- X backup = BACKUP;
- X cffile = CONFIG;
- X while ((c = getopt(argc, argv, "bc:devo:z")) != EOF) {
- X switch (c) {
- X case 'b': /* don't back up files larger than arg kbytes */
- X if ((maxsize = atoi(optarg)) < 1) {
- X fprintf(stderr,
- X "%s: illegal argument to -b: %d\n",
- X progname, optarg);
- X ++errflag;
- X } else
- X maxsize <<= 10; /* multiple of 1k */
- X break;
- X case 'c': /* specify alternate configuration file */
- X cffile = optarg;
- X break;
- X case 'd': /* debugging output */
- X ++debug;
- X break;
- X case 'e': /* copy executables */
- X ++doexecs;
- X break;
- X case 'v': /* be (more and more) verbose */
- X ++verbose;
- X break;
- X case 'o': /* specify alternate backup location */
- X backup = optarg;
- X break;
- X case 'z': /* fake it */
- X ++dryrun;
- X break;
- X default:
- X ++errflag;
- X }
- X }
- X (void) time(&now);
- X if ((cf_fp = fopen(cffile, "r+")) == NULL) {
- X fprintf(stderr, "%s: open(%s): %s\n",
- X progname, cffile, EMSG(errno) /* ... */);
- X Usage();
- X }
- X if (verbose
- X && flock(fileno(cf_fp), LOCK_EX|LOCK_NB) < 0
- X && errno == EWOULDBLOCK)
- X printf("waiting for exclusive lock on %s\n", cffile);
- X if (flock(fileno(cf_fp), LOCK_EX) < 0) {
- X fprintf(stderr, "%s: can't get lock on %s\n",
- X progname, cffile);
- X exit(1);
- X } else if (verbose > 1) {
- X setbuf(stdout, (char *)NULL);
- X printf("Start at %slocked %s\n", ctime(&now), cffile);
- X }
- X cf = readconfig(cf_fp);
- X if (optind == argc) {
- X /* figure out what we should back up */
- X for (cfp = cf; cfp != NULL; cfp = cfp->next) {
- X if (cfp->lasttime == 0) {
- X if (debug)
- X printf("%s: lasttime is 0\n", cfp->path);
- X continue;
- X }
- X /* give 5 minutes leeway */
- X if (cfp->lasttime + interval(cfp->interval) < now+300) {
- X /* add this path to the list */
- X if (debug)
- X printf("adding %s\n", cfp->path);
- X addpath(cfp);
- X } else if (debug)
- X printf("ignoring %s: lasttime=%d interval=%s now=%d\n",
- X cfp->path, cfp->lasttime, cfp->interval, now);
- X }
- X } else {
- X for (; optind < argc; ++optind) {
- X for (cfp = cf; cfp != NULL; cfp = cfp->next) {
- X if (cfp->lasttime == 0)
- X continue;
- X if (strcmp(cfp->path, argv[optind]) == 0) {
- X addpath(cfp);
- X break;
- X }
- X }
- X if (cfp == NULL) {
- X /* append new entry to config file */
- X for (cfp = cf; cfp != NULL && cfp->next != NULL;
- X cfp = cfp->next)
- X continue;
- X if (cfp != NULL) {
- X cfp->next = (struct config *)
- X emalloc((u_int)sizeof (struct config));
- X cfp = cfp->next;
- X cfp->next = NULL;
- X cfp->line = NULL;
- X cfp->interval = "24h";
- X cfp->path = argv[optind];
- X cfp->filter = "/dev/null";
- X }
- X cfp->lasttime = now - DELTA;
- X addpath(cfp);
- X }
- X }
- X }
- X
- X if ((devbackup = getdevice(backup, MNTTAB)) == NULL &&
- X (devbackup = getdevice(backup, MOUNTED)) == NULL) {
- X fprintf(stderr, "%s: could not determine backup device\n",
- X progname);
- X exit(1);
- X }
- X if ((backupmnt = getpath(devbackup, MNTTAB)) == NULL &&
- X (backupmnt = getpath(devbackup, MOUNTED)) == NULL) {
- X fprintf(stderr, "%s: could not determine mount point for %s\n",
- X progname, devbackup);
- X ++errflag;
- X }
- X if (errflag)
- X Usage();
- X (void) umask(0);
- X for (lp = devlist; lp != NULL; lp = lp->next)
- X if (doit(lp->paths, lp->text))
- X for (pp = lp->paths; pp != NULL; pp = pp->next)
- X pp->config->lasttime = now;
- X if (!dryrun)
- X writeconfig(cf_fp, cf);
- X (void) flock(fileno(cf_fp), LOCK_UN);
- X if (verbose > 1) {
- X time(&now);
- X printf("unlocked %s\nEnd at %s", cffile, ctime(&now));
- X }
- X (void) fclose(cf_fp);
- X#ifdef PROF
- X /* drop profiling data in a known place */
- X chdir("/tmp");
- X#endif
- X exit(0);
- X}
- X
- XUsage()
- X{
- X fprintf(stderr,
- X"Usage: %s [ -devz -b# ] [ -c backup.conf ] [ -o /backup ] [ /path ... ]\n",
- X progname);
- X exit(1);
- X}
- X
- X/*
- X * The filter files contain glob expressions, one per line. We need to
- X * keep track of which filter files we've read in, since several paths
- X * may share the same filters.
- X */
- X
- Xstruct filter {
- X char *name;
- X char *contents;
- X} filters[50];
- X
- Xint maxfilters = 0;
- X
- X/* return pointer to contents of a filter file stored away */
- X
- Xchar *
- Xreadfilter(file)
- X char *file;
- X{
- X int i, fd;
- X struct stat stbuf;
- X
- X if (strcmp(file, "/dev/null") == 0)
- X return NULL;
- X for (i = 0; i < maxfilters
- X && i < (sizeof filters/sizeof (struct filter)); ++i) {
- X if (strcmp(file, filters[i].name) == 0)
- X return filters[i].contents;
- X }
- X if (i == (sizeof filters/sizeof (struct filter))) {
- X fprintf(stderr, "%s: ran out of filters\n", progname);
- X exit(1);
- X }
- X if ((fd = open(file, O_RDONLY, 0)) < 0) {
- X fprintf(stderr, "%s: can't open filter file %s\n",
- X progname, file);
- X exit(1);
- X }
- X if (fstat(fd, &stbuf) < 0) {
- X fprintf(stderr, "%s: can't stat open filter file %s\n",
- X progname, file);
- X exit(1);
- X }
- X filters[i].contents = emalloc((u_int)stbuf.st_size+1);
- X if (read(fd, filters[i].contents, stbuf.st_size) < stbuf.st_size) {
- X fprintf(stderr, "%s: couldn't read %d bytes from %s\n",
- X progname, stbuf.st_size, file);
- X }
- X *(filters[i].contents+stbuf.st_size) = '\0';
- X filters[i].name = file;
- X ++maxfilters;
- X (void) close(fd);
- X return filters[i].contents;
- X}
- X
- X/*
- X * We maintain a two-level linked list structure (i.e. list of lists),
- X * associating paths with their raw device. When there are several paths
- X * on the same device, we want to handle them simultaneously and only
- X * do the ilist walking once per device. The root of this structure is
- X * devlist.
- X */
- X
- Xint
- Xaddpath(cfp)
- X struct config *cfp;
- X{
- X struct a_dev *lp;
- X struct a_path *pp;
- X char *rawdevice;
- X
- X if (cfp->path == NULL || *cfp->path != '/') {
- X fprintf(stderr, "%s: illegal path: %s\n",
- X progname,
- X cfp->path == NULL ? "(null)" : cfp->path);
- X } else if ((rawdevice = getdevice(cfp->path, MNTTAB)) == NULL) {
- X fprintf(stderr, "%s: no device for %s\n",
- X progname, cfp->path);
- X } else if (*rawdevice != '/') {
- X fprintf(stderr, "%s: bad device %s for %s\n",
- X progname, rawdevice, cfp->path);
- X } else {
- X /* link the path, device pair into lists */
- X for (lp = devlist; lp != NULL; lp = lp->next)
- X if (strcmp(lp->text, rawdevice) == 0)
- X break;
- X pp = (struct a_path *)
- X emalloc((u_int)sizeof (struct a_path));
- X pp->path = cfp->path;
- X pp->newer = cfp->lasttime;
- X pp->glob = readfilter(cfp->filter);
- X pp->config = cfp;
- X if (lp != NULL)
- X pp->next = lp->paths;
- X else {
- X lp = (struct a_dev *)
- X emalloc((u_int)sizeof (struct a_dev));
- X lp->next = devlist;
- X devlist = lp;
- X lp->text = emalloc((u_int)strlen(rawdevice)+1);
- X (void) strcpy(lp->text, rawdevice);
- X pp->next = NULL;
- X }
- X lp->paths = pp;
- X }
- X}
- X
- X/* return the name of the raw device corresponding to a particular file */
- X
- Xchar *
- Xgetdevice(path, table)
- X char *path;
- X char *table;
- X{
- X char *cp, *s;
- X struct mntent *me;
- X FILE *fp;
- X struct stat stpath, stdev;
- X
- X if (lstat(path, &stpath) < 0) {
- X fprintf(stderr, "%s: lstat(%s): %s\n",
- X progname, path, EMSG(errno));
- X return NULL;
- X }
- X if (!((stpath.st_mode & S_IFMT) & (S_IFREG | S_IFDIR))) {
- X fprintf(stderr, "%s: %s is a special file\n", progname, path);
- X return NULL;
- X }
- X if ((fp = setmntent(table, "r")) == NULL) {
- X fprintf(stderr, "%s: setmntent(%s): %s\n",
- X progname, table, EMSG(errno));
- X return NULL;
- X }
- X while ((me = getmntent(fp)) != NULL) {
- X if (strcmp(me->mnt_type, MNTTYPE_42) == 0
- X && strncmp(me->mnt_fsname, "/dev/", 5) == 0
- X && lstat(me->mnt_fsname, &stdev) == 0
- X && stdev.st_rdev == stpath.st_dev) {
- X (void) endmntent(fp);
- X cp = emalloc((u_int)strlen(me->mnt_fsname)+2);
- X (void) strcpy(cp, me->mnt_fsname);
- X s = cp + strlen(cp) + 1;
- X while (s > cp && *(s - 1) != '/')
- X *s = *(s-1), --s;
- X if (s > cp)
- X *s = 'r';
- X return cp;
- X }
- X }
- X (void) endmntent(fp);
- X return NULL;
- X}
- X
- X/* get the mount point of the filesystem on the raw device */
- X
- Xchar *
- Xgetpath(device, table)
- X char *device;
- X char *table;
- X{
- X char *cp;
- X struct mntent *me;
- X FILE *fp;
- X char devpath[MAXPATHLEN];
- X struct stat stpath, stdev;
- X extern char *rindex();
- X
- X (void) strcpy(devpath, device);
- X if ((cp = rindex(devpath, '/')) != NULL) {
- X ++cp;
- X if (*cp == 'r')
- X while ((*cp = *(cp+1)) != '\0')
- X ++cp;
- X }
- X if ((fp = setmntent(table, "r")) == NULL) {
- X fprintf(stderr, "%s: setmntent(%s): %s\n",
- X progname, table, EMSG(errno));
- X return NULL;
- X }
- X while ((me = getmntent(fp)) != NULL) {
- X if (strcmp(me->mnt_type, MNTTYPE_42) == 0
- X && strcmp(me->mnt_fsname, devpath) == 0
- X && lstat(me->mnt_fsname, &stdev) == 0
- X && lstat(me->mnt_dir, &stpath) == 0
- X && stdev.st_rdev == stpath.st_dev) {
- X (void) endmntent(fp);
- X cp = emalloc((u_int)strlen(me->mnt_dir)+1);
- X (void) strcpy(cp, me->mnt_dir);
- X return cp;
- X }
- X }
- X (void) endmntent(fp);
- X return NULL;
- X}
- X
- X#define sblock sb_un.u_sblock
- X
- Xstruct iinfo {
- X int inum; /* must be int so can be -ve too */
- X u_int blks;
- X time_t mtime;
- X};
- X
- Xint
- Xcmpiinfo(iip1, iip2)
- X register struct iinfo *iip1, *iip2;
- X{
- X return iip1->mtime - iip2->mtime;
- X}
- X
- Xstruct iinfo *stack[2];
- Xlong stacksize[2];
- Xlong needspace[2];
- Xint top = -1;
- X
- Xchar *dirmask;
- Xint mustfree;
- X
- X#define SET(v,i) ((v)[(i)/BITSPERBYTE] |= (1<<((i)%BITSPERBYTE)))
- X#define TST(v,i) ((v)[(i)/BITSPERBYTE] & (1<<((i)%BITSPERBYTE)))
- X
- Xint
- Xdoit(path, dev)
- X struct a_path *path;
- X char *dev;
- X{
- X register struct iinfo *st;
- X register int i, inum;
- X int fd, hiwater, lowater;
- X u_int nfiles;
- X union { struct fs u_sblock; char dummy[SBSIZE]; } sb_un;
- X char *bitvec, *dirvec, pathbuf[MAXPATHLEN];
- X struct a_path *pp;
- X struct statfs fsbuf;
- X extern int itest(), mkbackup(), rmbackup();
- X extern char *calloc();
- X
- X if (debug)
- X printf("doing %s\n", dev);
- X if ((fd = open(dev, O_RDONLY, 0)) < 0) {
- X fprintf(stderr, "%s: open(%s): %s\n",
- X progname, dev, EMSG(errno));
- X return 0;
- X }
- X if (bread(fd, SBLOCK, (char *)&sblock, (long) SBSIZE) < 0) {
- X fprintf(stderr, "%s: can't read superblock from %s: %s\n",
- X progname, dev, EMSG(errno));
- X return 0;
- X }
- X (void) close(fd);
- X nfiles = sblock.fs_ipg * sblock.fs_ncg;
- X stack[++top] = (struct iinfo *)calloc(nfiles, sizeof (struct iinfo));
- X stacksize[top] = 0;
- X needspace[top] = 0;
- X dirvec = calloc((nfiles/BITSPERBYTE)+1, 1);
- X dirmask = dirvec;
- X if (top == 0) {
- X /* figure out the oldest lasttime before i-list walk */
- X lasttime = now;
- X for (pp = path; pp != NULL; pp = pp->next)
- X if (pp->newer < lasttime)
- X lasttime = pp->newer;
- X }
- X if (debug)
- X printf("%s: scan %d inodes\n", dev, nfiles);
- X (void) ilw(dev, itest, 1);
- X if (verbose)
- X printf("%s: found %d candidate files, with %d blocks total\n",
- X dev, stacksize[top], needspace[top]);
- X if (stacksize[top] == 0) {
- X (void) free(dirvec);
- X (void) free((char *)stack[top--]);
- X return 0;
- X }
- X bitvec = calloc((nfiles/BITSPERBYTE)+1, 1);
- X if (top == 0) {
- X /*
- X * This is the filesystem we want to back up.
- X * First make sure there is enough free space in the backup
- X * filesystem (if not, call myself recursively), then run
- X * a file tree walker to copy the indicated files to the
- X * backup filesystem.
- X */
- X if (statfs(backup, &fsbuf) < 0) {
- X fprintf(stderr, "%s: statfs(%s): %s\n",
- X progname, backup, EMSG(errno));
- X exit(1);
- X }
- X hiwater = (fsbuf.f_blocks-fsbuf.f_bfree
- X +fsbuf.f_bavail)*HIWATER/100;
- X if (fsbuf.f_blocks - fsbuf.f_bfree + needspace[top] > hiwater) {
- X /* need to free some space */
- X struct a_path backupdesc;
- X /*
- X * If you want to free so free space will be at
- X * LOWATER after backup finishes, then enable the
- X * next line and do s/hiwater/lowater/ in the
- X * following line defining mustfree.
- X */
- X /* lowater = +(hiwater*LOWATER)/HIWATER; */
- X mustfree = fsbuf.f_blocks - fsbuf.f_bfree
- X + needspace[top] - hiwater;
- X /* select all files */
- X lasttime = 0;
- X maxsize = sblock.fs_dsize * DEV_BSIZE;
- X backupdesc.path = backup;
- X backupdesc.next = NULL;
- X backupdesc.glob = NULL;
- X backupdesc.newer = lasttime;
- X if (!doit(&backupdesc, devbackup)) {
- X fprintf(stderr,
- X "%s: Can't walk %s to free space\n",
- X progname, backup);
- X exit(1);
- X }
- X }
- X for (i = 0, st = stack[top]; i < nfiles; ++i, ++st) {
- X if (st->mtime > 0) {
- X SET(bitvec, st->inum);
- X }
- X }
- X for (; path != NULL; path = path->next) {
- X if (chdir(path->path) < 0) {
- X fprintf(stderr, "%s: chdir(%s): %s\n",
- X progname, path->path, EMSG(errno));
- X exit(1);
- X }
- X (void) sprintf(pathbuf, "%s/", path->path);
- X lasttime = path->newer;
- X if (verbose)
- X printf("%s: select mtime within %d sec in %s\n",
- X dev, now - lasttime, path->path);
- X walk(pathbuf, pathbuf + strlen(pathbuf), path->glob,
- X mkbackup, bitvec, dirvec);
- X }
- X } else {
- X /*
- X * This is the backup filesystem.
- X * Sort the inodes selected into oldest-first, then run
- X * a file tree walker to delete the files until we have
- X * enough space *and* are under the low water mark.
- X */
- X
- X /* assert strcmp(path->path, backup) == 0 */
- X /* compress the inode array */
- X st = stack[top];
- X for (i = inum = 0; i < stacksize[top]; ++inum) {
- X if ((st+inum)->mtime > 0) {
- X (st+i)->inum = inum;
- X (st+i)->blks = (st+inum)->blks;
- X (st+i)->mtime = (st+inum)->mtime;
- X ++i;
- X }
- X }
- X if (chdir(path->path) < 0) {
- X fprintf(stderr, "%s: chdir(%s): %s\n",
- X progname, path->path, EMSG(errno));
- X exit(1);
- X }
- X (void) sprintf(pathbuf, "%s/", path->path);
- X if (strcmp(backup, backupmnt) != 0) {
- X /* backup area is not an entire filesystem */
- X walk(pathbuf, pathbuf + strlen(pathbuf), (char *)NULL,
- X (int (*)())NULL, (char *)NULL, dirvec);
- X /* now all possible inums are negative and rest +ve */
- X st = stack[top];
- X for (i = inum = 0; i < stacksize[top]; ++inum) {
- X if ((st+inum)->inum < 0) {
- X (st+i)->inum = inum;
- X ++i;
- X } else
- X (st+i)->inum = 0;
- X }
- X /* now all possible inums are +ve and rest 0 */
- X }
- X /* sort it oldest first */
- X qsort((char *)stack[top], stacksize[top],
- X sizeof (struct iinfo), cmpiinfo);
- X /* mustfree has been set in our parent doit() */
- X /* go from oldest to newest, truncate after mustfree blocks */
- X st = stack[top];
- X for (i = 0; i < stacksize[top] && mustfree > 0; ++i, ++st) {
- X if (st->inum > 2 && st->mtime > 0) {
- X mustfree -= st->blks;
- X SET(bitvec, st->inum);
- X }
- X }
- X (void) sprintf(pathbuf, "%s/", path->path);
- X walk(pathbuf, pathbuf + strlen(pathbuf), (char *)NULL,
- X rmbackup, bitvec, dirvec);
- X }
- X (void) free(bitvec);
- X (void) free(dirvec);
- X (void) free((char *)stack[top--]);
- X return 1;
- X}
- X
- X/* This routine is used by the inode list walker) to test for relevant inodes */
- X
- Xitest(ip, inum)
- X struct dinode *ip;
- X int inum;
- X{
- X register struct iinfo *iip;
- X
- X if ((ip->di_mode & S_IFMT) == S_IFREG
- X && ip->di_mtime > lasttime
- X && ip->di_size < maxsize
- X && (doexecs || (ip->di_mode & 07111) == 0)) {
- X /* we have a candidate for backing up */
- X iip = stack[top] + inum;
- X iip->inum = inum;
- X stacksize[top] += 1;
- X needspace[top] += (iip->blks = ip->di_blocks);
- X iip->mtime = ip->di_mtime;
- X /*
- Xprintf("%6d:\tmode=0%o uid=%d gid=%d size=%d nlink=%d, a=%D m=%D c=%D\n",
- X inum, ip->di_mode, ip->di_uid, ip->di_gid, ip->di_size,
- X ip->di_nlink, ip->di_atime, ip->di_mtime, ip->di_ctime);
- X */
- X } else if ((ip->di_mode & S_IFMT) == S_IFDIR)
- X SET(dirmask, inum);
- X return 0;
- X}
- X
- X/*
- X * Create all the directories down a particular backup path, copying stat
- X * info from the original directories.
- X */
- X
- Xcreatdirs(dirpath, origpath, stbufp)
- X char *dirpath, *origpath;
- X struct stat *stbufp;
- X{
- X char *cp;
- X struct stat stbuf;
- X extern char *rindex();
- X
- X if (mkdir(dirpath, stbufp->st_mode & 0777) < 0) {
- X if (errno == ENOENT) {
- X if ((cp = rindex(dirpath, '/')) > dirpath) {
- X *cp = '\0';
- X creatdirs(dirpath, origpath, stbufp);
- X *cp = '/';
- X }
- X (void) mkdir(dirpath, (stbufp->st_mode & 0777)|0111);
- X if (stat(origpath, &stbuf) == 0) {
- X (void) chown(dirpath, stbuf.st_uid,
- X stbuf.st_gid);
- X if (stbuf.st_mode & 0400)
- X stbuf.st_mode |= 0100;
- X if (stbuf.st_mode & 040)
- X stbuf.st_mode |= 010;
- X if (stbuf.st_mode & 04)
- X stbuf.st_mode |= 01;
- X (void) chmod(dirpath, stbuf.st_mode & 0777);
- X } else
- X fprintf(stderr, "%s: stat(%s): %s\n",
- X progname, origpath, EMSG(errno));
- X }
- X } else if (stat(origpath, &stbuf) == 0) {
- X (void) chown(dirpath, stbuf.st_uid, stbuf.st_gid);
- X if (stbuf.st_mode & 0400)
- X stbuf.st_mode |= 0100;
- X if (stbuf.st_mode & 040)
- X stbuf.st_mode |= 010;
- X if (stbuf.st_mode & 04)
- X stbuf.st_mode |= 01;
- X (void) chmod(dirpath, stbuf.st_mode & 0777);
- X } else
- X fprintf(stderr, "%s: stat(%s): %s\n",
- X progname, origpath, EMSG(errno));
- X}
- X
- X/* Create an actual backup file, return its file descriptor */
- X
- Xint
- Xcreatbackup(path, stbufp, filename)
- X char *path, *filename;
- X struct stat *stbufp;
- X{
- X int fd;
- X char *cp, *ct;
- X
- X ct = ctime(&stbufp->st_mtime);
- X (void) sprintf(filename, "%s%s/", backup, path);
- X cp = filename + strlen(filename);
- X *cp++ = ct[4]; *cp++ = ct[5]; *cp++ = ct[6];
- X *cp++ = ct[8] == ' ' ? '0' : ct[8]; *cp++ = ct[9];
- X *cp++ = '-';
- X *cp++ = ct[11]; *cp++ = ct[12]; *cp++ = ct[13];
- X *cp++ = ct[14]; *cp++ = ct[15]; *cp = '\0';
- X fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, stbufp->st_mode & 0777);
- X if (fd < 0) {
- X if (errno == ENOENT) {
- X cp = rindex(filename, '/');
- X *cp = '\0';
- X creatdirs(filename, filename+strlen(backup), stbufp);
- X *cp = '/';
- X }
- X fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC,
- X stbufp->st_mode & 0777);
- X }
- X if (fd < 0) {
- X fprintf(stderr, "%s: open(%s): %s\n",
- X progname, filename, EMSG(errno));
- X return -1;
- X }
- X (void) fchown(fd, stbufp->st_uid, stbufp->st_gid);
- X return fd;
- X}
- X
- X/* This routine called from walk() to make a backup of a file */
- X
- Xint
- Xmkbackup(path, glob)
- X char *path, *glob;
- X{
- X struct stat stbuf;
- X int n, bfd, fd;
- X char bigbuf[8*1024], bpath[MAXPATHLEN];
- X struct timeval tv[2];
- X
- X if (doglob(glob, path))
- X return;
- X if ((fd = open(path, O_RDONLY, 0)) < 0) {
- X /*
- X * File may have been removed under our feet.
- X * Don't make noise about that.
- X */
- X if (errno == ENOENT)
- X return;
- X fprintf(stderr, "%s: open(%s): %s\n",
- X progname, path, EMSG(errno));
- X return;
- X }
- X if (fstat(fd, &stbuf) < 0) {
- X fprintf(stderr, "%s: fstat(%s): %s\n",
- X progname, path, EMSG(errno));
- X (void) close(fd);
- X return;
- X }
- X if (stbuf.st_mtime < lasttime) {
- X (void) close(fd);
- X return;
- X }
- X if (stbuf.st_size == 0) {
- X (void) close(fd);
- X return;
- X }
- X if (dryrun || verbose > 1)
- X printf("copy %s\n", path);
- X if (dryrun) {
- X (void) close(fd);
- X return;
- X }
- X if ((bfd = creatbackup(path, &stbuf, bpath)) < 0) {
- X (void) close(fd);
- X return;
- X }
- X while ((n = read(fd, bigbuf, sizeof bigbuf)) > 0)
- X if (write(bfd, bigbuf, n) < n) {
- X fprintf(stderr, "%s: write error: %s\n",
- X progname, EMSG(errno));
- X /* saving a little bit is better than nothing... */
- X break;
- X }
- X (void) close(fd);
- X (void) close(bfd);
- X /* Preserve file times, so "find /backup -newer ..." works */
- X tv[0].tv_sec = stbuf.st_atime;
- X tv[1].tv_sec = stbuf.st_mtime;
- X tv[0].tv_usec = tv[1].tv_usec = 0;
- X (void) utimes(bpath, tv);
- X}
- X
- Xstatic int rmcnt;
- X
- X/* This routine is called from walk() to rm a backup file (and parent dir) */
- X
- X/* ARGSUSED */
- Xint
- Xrmbackup(path, glob)
- X char *path, *glob;
- X{
- X
- X if (dryrun || verbose > 1)
- X printf("remove %s\n", path);
- X if (dryrun)
- X return;
- X (void) unlink(path);
- X ++rmcnt;
- X}
- X
- X/* a file tree walker (a la ftw(3)) for this application (see ftw(3) BUGS) */
- X
- Xwalk(path, cp, glob, fn, vector, dirvec)
- X char *path, *cp, *glob;
- X int (*fn)();
- X char *vector, *dirvec;
- X{
- X register struct direct *dp;
- X register DIR *dirp;
- X register char *eos;
- X register int n;
- X
- X if ((dirp = opendir(".")) == NULL) {
- X fprintf(stderr, "%s: opendir(%s): %s\n",
- X progname, path, EMSG(errno));
- X /* error is usually "too many open files", so don't exit */
- X return;
- X }
- X for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
- X if (dp->d_name[0] == '.'
- X && (dp->d_namlen == 1
- X || (dp->d_name[1] == '.' && dp->d_namlen == 2)))
- X continue;
- X if (vector == NULL) /* magic for backup partition */
- X (stack[top]+dp->d_fileno)->inum = -dp->d_fileno;
- X if (vector != NULL && TST(vector, dp->d_fileno)) {
- X (void) strcpy(cp, dp->d_name);
- X (*fn)(path, glob);
- X } else if (TST(dirvec, dp->d_fileno)
- X && chdir(dp->d_name) == 0) {
- X (void) strcpy(cp, dp->d_name);
- X eos = cp + dp->d_namlen;
- X *eos++ = '/';
- X *eos = '\0';
- X n = rmcnt;
- X walk(path, eos, glob, fn, vector, dirvec);
- X (void) chdir("..");
- X if (fn == rmbackup && n != rmcnt) {
- X *--eos = '\0'; /* clobber trailing '/' */
- X (void) rmdir(path);
- X }
- X }
- X }
- X (void) closedir(dirp);
- X}
- X
- X/* Malloc that prints error and dies if it can't honour memory request */
- X
- Xchar *
- Xemalloc(n)
- X u_int n;
- X{
- X char *cp;
- X extern char *malloc();
- X
- X if ((cp = malloc(n)) == NULL) {
- X fprintf(stderr, "%s: malloc failure!\n", progname);
- X exit(1);
- X }
- X return cp;
- X}
- X
- X/* Read and parse the configuration file */
- X
- Xstruct config *
- Xreadconfig(fp)
- X FILE *fp;
- X{
- X struct config *cfe, *cfhead, **pcfenp;
- X char *cp, *s, buf[BUFSIZ];
- X
- X cfhead = NULL;
- X pcfenp = &cfhead;
- X rewind(fp);
- X while (fgets(buf, sizeof buf, fp) != NULL) {
- X cfe = (struct config *)emalloc((u_int)sizeof (struct config));
- X cfe->line = emalloc((u_int)strlen(buf));
- X (void) strncpy(cfe->line, buf, strlen(buf)-1);
- X cfe->next = NULL;
- X cp = buf;
- X while (isascii(*cp) && isspace(*cp))
- X ++cp;
- X s = cp;
- X while (isascii(*cp) && !isspace(*cp))
- X ++cp;
- X if (*s != '#')
- X *cp++ = '\0';
- X cfe->path = emalloc((u_int)strlen(s)+1);
- X (void) strcpy(cfe->path, s);
- X if (*s == '#') {
- X *(cfe->path+strlen(s)-1) = '\0'; /* kill NL */
- X cfe->lasttime = 0;
- X *pcfenp = cfe;
- X pcfenp = &cfe->next;
- X continue;
- X }
- X while (isascii(*cp) && isspace(*cp))
- X ++cp;
- X s = cp;
- X while (isascii(*cp) && !isspace(*cp))
- X ++cp;
- X *cp++ = '\0';
- X cfe->interval = emalloc((u_int)strlen(s)+1);
- X (void) strcpy(cfe->interval, s);
- X while (isascii(*cp) && isspace(*cp))
- X ++cp;
- X s = cp;
- X while (isascii(*cp) && !isspace(*cp))
- X ++cp;
- X *cp++ = '\0';
- X cfe->filter = emalloc((u_int)strlen(s)+1);
- X (void) strcpy(cfe->filter, s);
- X while (isascii(*cp) && isspace(*cp))
- X ++cp;
- X /* interpret ctime */
- X *(cfe->line + (cp - buf)) = '\0';
- X cfe->lasttime = ctime2time(cp);
- X *pcfenp = cfe;
- X pcfenp = &cfe->next;
- X }
- X return cfhead;
- X}
- X
- X/* Write the configuration file out with the updated information */
- X
- Xvoid
- Xwriteconfig(fp, cfp)
- X FILE *fp;
- X struct config *cfp;
- X{
- X rewind(fp);
- X for (; cfp != NULL; cfp = cfp->next) {
- X if (cfp->lasttime == 0) {
- X fprintf(fp, "%s\n", cfp->path); /* comment */
- X continue;
- X }
- X if (cfp->line == NULL) /* new entry */
- X fprintf(fp, "%s\t%s\t%s\t%s",
- X cfp->path, cfp->interval, cfp->filter,
- X ctime(&cfp->lasttime));
- X else
- X fprintf(fp, "%s%s", cfp->line, ctime(&cfp->lasttime));
- X }
- X}
- X
- X/* Parse a ctime() string into seconds since epoch */
- X
- Xchar *ap[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 0 };
- X
- Xstruct timezone tz = { 0 };
- X
- Xint
- Xctime2time(s)
- X char *s;
- X{
- X static int isdst, flag = 0;
- X int sec, century, year, month, dayinmonth, julian, i;
- X struct timeval tv;
- X struct tm *tms;
- X
- X if (strlen(s) < 25)
- X return 0;
- X if (!flag) {
- X (void) gettimeofday(&tv, &tz);
- X tms = localtime(&now);
- X isdst = tms->tm_isdst;
- X flag = 1;
- X }
- X century = atoi(s+20)/100;
- X year = atoi(s+22);
- X dayinmonth = atoi(s+8);
- X for (i = 0; ap[i] != NULL; ++i) {
- X if (strncmp(s+4, ap[i], 3) == 0)
- X break;
- X }
- X if (ap[i] == NULL)
- X month = -1;
- X else {
- X if ((month = ++i) > 2)
- X month -= 3;
- X else
- X month += 9, year--;
- X }
- X sec = atoi(s+17) + 60*(atoi(s+14) + 60*atoi(s+11));
- X /* this is a standard julian date formula of unknown origin */
- X julian = (146097L * century)/4L + (1461L * year)/4L
- X + (153L * month + 2L)/5L + dayinmonth - 719469L;
- X sec += julian * 24 * 60 * 60 + (tz.tz_minuteswest-(isdst*60))*60;
- X return sec;
- X}
- X
- X/* Parse an interval string, e.g. 2h30m or 8h or 15s, the obvious meanings */
- X
- Xint
- Xinterval(s)
- X char *s;
- X{
- X int i, sec;
- X
- X if (s == NULL)
- X return 0;
- X i = sec = 0;
- X while (*s != '\0' && isascii(*s)) {
- X if (isdigit(*s)) {
- X i *= 10;
- X i += *s - '0';
- X } else if (*s == 'h')
- X sec += 3600*i, i = 0;
- X else if (*s == 'm')
- X sec += 60*i, i = 0;
- X else if (*s == 's')
- X sec += i, i = 0;
- X ++s;
- X }
- X return sec;
- X}
- X
- X/* Test if path is de-selected by the glob patterns in the filter string */
- X
- Xint
- Xdoglob(filter, path)
- X char *filter, *path;
- X{
- X register char *cp;
- X
- X for (cp = filter; cp != NULL && *cp != '\0';) {
- X if (match(cp, path))
- X return 1;
- X while (*cp != '\n' && *cp != '\0')
- X ++cp;
- X if (*cp == '\n')
- X ++cp;
- X }
- X return 0;
- X}
- X
- X/* General glob pattern match routine, customized to exit on newline */
- X
- Xint
- Xmatch(pattern, string)
- X register char *pattern, *string;
- X{
- X while (1)
- X switch (*pattern) {
- X case '*':
- X ++pattern;
- X do {
- X if (match(pattern, string))
- X return 1;
- X } while (*string++ != '\0');
- X return 0;
- X break;
- X case '[':
- X if (*string == '\0')
- X return 0;
- X while ((*++pattern != ']') && (*pattern != *string))
- X if (*pattern == '\0')
- X return 0;
- X if (*pattern == ']')
- X return 0;
- X while (*pattern++ != ']')
- X if (*pattern == '\0')
- X return 0;
- X ++string;
- X break;
- X case '?':
- X ++pattern;
- X if (*string++ == '\0')
- X return 0;
- X break;
- X case '\n':
- X return (*string == '\0');
- X default:
- X if (*pattern++ != *string++)
- X return 0;
- X }
- X}
- END_OF_backup.c
- if test 28044 -ne `wc -c <backup.c`; then
- echo shar: \"backup.c\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f backup.conf -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"backup.conf\"
- else
- echo shar: Extracting \"backup.conf\" \(363 characters\)
- sed "s/^X//" >backup.conf <<'END_OF_backup.conf'
- X# path intvl filter file last done
- X/homes/neat/car 8h /ai/etc/backup.filter Tue May 23 12:37:01 1989
- X/homes/neat/cdr 8h /ai/etc/backup.filter Tue May 23 12:37:01 1989
- X/homes/neat/lambda 8h /ai/etc/backup.filter Tue May 23 12:37:01 1989
- X/homes/neat/eq 8h /ai/etc/backup.filter Tue May 23 12:37:01 1989
- X/sys 8h /ai/etc/backup.filter Tue May 23 12:37:01 1989
- END_OF_backup.conf
- if test 363 -ne `wc -c <backup.conf`; then
- echo shar: \"backup.conf\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f backup.filter -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"backup.filter\"
- else
- echo shar: Extracting \"backup.filter\" \(109 characters\)
- sed "s/^X//" >backup.filter <<'END_OF_backup.filter'
- X*/core
- X*/a.out
- X*/*.o
- X*/bin*
- X*/#*
- X*/*~
- X*/.msg?rc
- X*/.rnlast
- X*/.rnsoft
- X*/.oldnewsrc
- X*/mbox
- X*/.timecheck
- X*/*.dvi
- END_OF_backup.filter
- if test 109 -ne `wc -c <backup.filter`; then
- echo shar: \"backup.filter\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f getback.1 -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"getback.1\"
- else
- echo shar: Extracting \"getback.1\" \(806 characters\)
- sed "s/^X//" >getback.1 <<'END_OF_getback.1'
- X.TH GETBACK 1 "UofToronto"
- X.SH NAME
- Xgetback \- Recover a backed up version of a file.
- X.SH SYNOPSIS
- X.B getback
- X[ -o
- X.I /backup
- X]
- X.I filename
- X.SH DESCRIPTION
- X.I Getback
- Xfinds the the copies made of a given filename made by the backup(8)
- Xprogram and allows them to be restored.
- XThe default backup tree can be overridden with the command line option.
- X.PP
- XOnce the backup copies are found, all of them are printed in a list and
- Xyou are asked which one you would like to restore. If a file with the same
- X.I filename
- Xalready exists, you are asked to confirm before
- X.I getback
- Xwill overwrite it.
- X.SH FILES
- X.ta 2i
- X/backup - root of the backup filesystem
- X.SH "SEE ALSO"
- X.IR backup (8)
- X.SH "INFO"
- XBased on the original program by Larry Philps, this is a reimplementation
- Xby Rayan Zachariassen, both at U of Toronto.
- END_OF_getback.1
- if test 806 -ne `wc -c <getback.1`; then
- echo shar: \"getback.1\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f getback.c -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"getback.c\"
- else
- echo shar: Extracting \"getback.c\" \(5861 characters\)
- sed "s/^X//" >getback.c <<'END_OF_getback.c'
- X/*
- X * Getback - retrieve files stored away by the incremental backup mechanism.
- X *
- X * Based on an original program by Larry Philps. This reimplementation is
- X * Copyright 1988 by Rayan Zachariassen, solely to prevent you from selling
- X * it or putting your name on it. Free (gratis) redistribution is encouraged.
- X */
- X
- X#include <stdio.h>
- X#include <ctype.h>
- X#include <signal.h>
- X#include <sys/param.h>
- X#include <sys/types.h>
- X#include <sys/file.h>
- X#include <sys/stat.h>
- X#include <sys/dir.h>
- X
- X#define BACKUP "/backup" /* root of backup hierarchy */
- X
- Xstruct copy {
- X char *file;
- X struct stat stbuf;
- X struct copy *next;
- X};
- X
- X#define EMSG(x) (errno < sys_nerr ? sys_errlist[x] : \
- X (sprintf(errmsgbuf, "unknown error %d", errno), errmsgbuf))
- X
- Xextern int errno, sys_nerr;
- Xextern char *sys_errlist[];
- Xchar errmsgbuf[30];
- X
- Xchar *progname;
- X
- Xextern int optind;
- Xextern char *optarg;
- X
- Xmain(argc, argv)
- X int argc;
- X char *argv[];
- X{
- X int c, errflag, i, copyindex, copyit, ifd, ofd, myuid;
- X char *backup, *buf, line[BUFSIZ];
- X DIR *dirp;
- X struct direct *dp;
- X struct copy *cep, *copyarr[1000];
- X struct stat stbuf;
- X char path[MAXPATHLEN], filename[MAXPATHLEN];
- X extern int cmpcopy();
- X extern int errno;
- X extern char *emalloc(), *sbrk();
- X
- X progname = argv[0];
- X backup = BACKUP;
- X errflag = 0;
- X while ((c = getopt(argc, argv, "o:")) != EOF) {
- X switch (c) {
- X case 'o':
- X backup = optarg;
- X break;
- X default:
- X ++errflag;
- X }
- X }
- X if (optind != argc - 1) {
- X fprintf(stderr, "%s: missing filename\n", progname);
- X ++errflag;
- X }
- X if (errflag) {
- X fprintf(stderr, "Usage: %s [ -o /backup ] filename\n",
- X progname);
- X exit(1);
- X }
- X if (argv[optind][0] == '/')
- X (void) strcpy(filename, argv[optind]);
- X else if (getwd(filename) == NULL) {
- X fprintf(stderr, "%s: %s\n", progname, filename);
- X exit(1);
- X } else
- X (void) sprintf(filename+strlen(filename), "/%s", argv[optind]);
- X (void) sprintf(path, "%s/%s", backup, filename);
- X if ((dirp = opendir(path)) == NULL) {
- X printf("There are no online backups of %s\n", argv[optind]);
- X exit(1);
- X }
- X copyindex = 0;
- X myuid = getuid();
- X for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
- X if (dp->d_namlen == sizeof "Jan01-00:00" - 1
- X && dp->d_name[5] == '-' && dp->d_name[8] == ':') {
- X (void) sprintf(path, "%s/%s/%s", backup, filename, dp->d_name);
- X if (stat(path, &stbuf) < 0)
- X continue;
- X if (myuid != 0 && myuid != stbuf.st_uid)
- X continue;
- X cep = (struct copy *)emalloc((u_int)sizeof (struct copy));
- X cep->file = emalloc(strlen(path)+1);
- X (void) strcpy(cep->file, path);
- X cep->stbuf = stbuf;
- X copyarr[copyindex++] = cep;
- X }
- X }
- X (void) closedir(dirp);
- X if (copyindex == 0) {
- X printf("There are no online backups of \"%s\"\n", argv[optind]);
- X exit(1);
- X } else if (copyindex == 1) {
- X printf("There is only one backup of \"%s\", dated %s",
- X argv[optind], ctime(©arr[0]->stbuf.st_mtime));
- X printf("Retrieve this copy [y] ? ");
- X fflush(stdout);
- X (void) gets(line);
- X if (line[0] == '\0' || line[0] == 'y' || line[0] == 'Y') {
- X copyit = 0;
- X } else {
- X fprintf(stderr, "%s: nothing changed\n", progname);
- X exit(1);
- X }
- X } else {
- X printf("There are %d backup versions of \"%s\" from:\n\n",
- X copyindex, argv[optind]);
- X qsort((char *)copyarr, copyindex, sizeof copyarr[0], cmpcopy);
- X for (i = 0; i < copyindex; ++i) {
- X printf("\t%2d.\t%.24s\t(%ld bytes)\n",
- X i+1, ctime(©arr[i]->stbuf.st_mtime),
- X copyarr[i]->stbuf.st_size);
- X }
- X (void) putchar('\n');
- X retry:
- X printf("Enter number corresponding to version you want [%d]: ",
- X copyindex);
- X fflush(stdout);
- X if (gets(line) == NULL)
- X line[0] = 'n';
- X if (line[0] == '\0')
- X copyit = copyindex - 1;
- X else if (line[0] == 'n' || line[0] == 'N') {
- X fprintf(stderr, "%s: nothing changed\n", progname);
- X exit(1);
- X } else if (!isdigit(line[0])
- X || (i = atoi(line)) <= 0 || i > copyindex) {
- X printf("Answer must be a number from 1-%d, or <cr>.\n",
- X copyindex);
- X goto retry;
- X } else
- X copyit = i-1;
- X }
- X if (stat(argv[optind], &stbuf) == 0) {
- X if (stbuf.st_uid != myuid) {
- X fprintf(stderr, "%s: you are not owner of %s\n",
- X progname, argv[optind]);
- X exit(1);
- X }
- X printf("\"%s\" exists, overwrite [y] ? ", argv[optind]);
- X (void) gets(line);
- X if (!(line[0] == '\0' || line[0] == 'y' || line[0] == 'Y')) {
- X fprintf(stderr, "%s: nothing changed\n", progname);
- X exit(1);
- X }
- X }
- X printf("Retrieving %.24s version of \"%s\" ... ",
- X ctime(©arr[copyit]->stbuf.st_mtime),
- X argv[optind]);
- X if ((buf = sbrk(copyarr[copyit]->stbuf.st_blksize)) == NULL) {
- X fprintf(stderr, "%s: sbrk(%d): %s\n",
- X progname, copyarr[copyit]->stbuf.st_blksize,
- X EMSG(errno));
- X exit(1);
- X }
- X if ((ifd = open(copyarr[copyit]->file, O_RDONLY, 0)) < 0) {
- X fprintf(stderr, "%s: open(%s): %s\n",
- X progname, copyarr[copyit]->file, EMSG(errno));
- X exit(1);
- X }
- X
- X (void) sigsetmask(sigmask(SIGHUP)
- X | sigmask(SIGINT)
- X | sigmask(SIGQUIT)
- X | sigmask(SIGTERM)
- X | sigmask(SIGTSTP));
- X
- X if ((ofd = open(argv[optind], O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
- X fprintf(stderr, "%s: open(%s): %s\n",
- X progname, argv[optind], EMSG(errno));
- X exit(1);
- X }
- X (void) umask(0);
- X (void) fchmod(ofd, copyarr[copyit]->stbuf.st_mode);
- X (void) fchown(ofd, copyarr[copyit]->stbuf.st_uid,
- X copyarr[copyit]->stbuf.st_gid);
- X while ((i = read(ifd, buf, copyarr[copyit]->stbuf.st_blksize)) > 0)
- X if (write(ofd, buf, i) < i) {
- X fprintf(stderr, "%s: write of %d bytes: %s\n",
- X progname, i, EMSG(errno));
- X exit(1);
- X }
- X (void) close(ofd);
- X (void) close(ifd);
- X printf("done!\n");
- X exit(0);
- X}
- X
- Xint
- Xcmpcopy(a, b)
- X struct copy **a, **b;
- X{
- X return (*a)->stbuf.st_mtime - (*b)->stbuf.st_mtime;
- X}
- X
- Xchar *
- Xemalloc(n)
- X u_int n;
- X{
- X char *cp;
- X extern char *malloc();
- X
- X if ((cp = malloc(n)) == NULL) {
- X fprintf(stderr, "%s: malloc(%u) failed!\n", progname, n);
- X exit(1);
- X }
- X return cp;
- X}
- END_OF_getback.c
- if test 5861 -ne `wc -c <getback.c`; then
- echo shar: \"getback.c\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f getback.sh -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"getback.sh\"
- else
- echo shar: Extracting \"getback.sh\" \(2205 characters\)
- sed "s/^X//" >getback.sh <<'END_OF_getback.sh'
- X#!/bin/sh
- X# getback [-o /backup] filename
- X
- XPATH=/bin:/usr/bin:/usr/ucb; export PATH
- XBACKUP=/backup
- Xmyname=`basename $0`
- XTMP=/tmp/${myname}$$
- XUSER=${USER-`whoami`} || exit
- X
- Xusage() {
- X echo "Usage: $myname [-o /backup] filename" 1>&2
- X exit 1
- X}
- Xsorry() {
- X echo "Sorry, there are no online backups of $filename"
- X exit 1
- X}
- Xgiveup() {
- X echo "$myname: nothing changed"
- X exit 1
- X}
- X
- Xset -- `getopt o: $*`
- Xif [ $? != 0 ]; then
- X usage
- Xfi
- Xfor i in $*; do
- X case $i in
- X -o) BACKUP=$2; shift 2;;
- X --) shift; break;;
- X esac
- Xdone
- X
- Xif [ $# != 1 ]; then
- X echo "$myname: missing filename" 1>&2
- X usage
- Xfi
- X
- Xfilename=$1
- Xcase $filename in
- X/*) backdir=$BACKUP$1;;
- X*) backdir=$BACKUP`/bin/pwd`/$1;;
- Xesac
- X
- Xtest -d $backdir || sorry
- Xtrap "/bin/rm -f $TMP; exit" 0 1 2 15
- X# We could ensure that the backup filenames look reasonable here...
- Xls -lrt $backdir | awk "\$1 ~ /^-/ && \$3 == \"$USER\"" >$TMP
- Xncopies=`sed -n '$=' $TMP`
- Xcase $ncopies in
- X1)
- X set -- `cat $TMP`
- X echo "There is only one backup of \"$filename\", dated $5 $6 $7"
- X echo -n "Retrieve this copy [y] ? "
- X read ans
- X case "$ans" in
- X "" | y* | Y* ) ;;
- X *) giveup;;
- X esac
- X ;;
- X[0-9]*)
- X echo "There are $ncopies backup versions of \"$filename\" from:"
- X echo ""
- X awk '{printf "\t%2d.\t%s %2d %s\t(%d bytes)\n", NR,$5,$6,$7,$4}' $TMP
- X echo ""
- X while :; do
- X echo -n "Enter number corresponding to the version you want [$ncopies] "
- X read ans
- X case "$ans" in
- X "")
- X version=$ncopies
- X break;;
- X n* | N*)
- X giveup;;
- X [0-9]*)
- X if [ $ans -gt 0 -a $ans -le $ncopies ]; then
- X version=$ans
- X break
- X fi;;
- X esac
- X echo "Answer must be a number from 1 to $ncopies, or <cr>."
- X done
- X set -- `sed -n ${version}p $TMP`
- X ;;
- X*)
- X sorry;;
- Xesac
- Xif [ -f $filename ]; then
- X owner=`ls -l $filename | awk '{print $3}'`
- X if [ $owner != $USER ]; then
- X echo "$myname: you are not the owner of $filename"
- X exit 1
- X fi
- X echo -n "\"$filename\" exists, overwrite [y] ? "
- X read ans
- X case "$ans" in
- X "" | y* | Y* );;
- X *) giveup;;
- X esac
- Xfi
- Xecho -n "Retrieving $5 $6 $7 version of \"$filename\" ... "
- Xcp -p $backdir/$8 $filename || {
- X echo $myname: copy failed
- X exit 1
- X}
- X# Update the times, but keep the modes
- Xtouch -f $filename || echo $myname: Can\'t touch \"$filename\"
- Xecho "done!"
- Xexit 0
- END_OF_getback.sh
- if test 2205 -ne `wc -c <getback.sh`; then
- echo shar: \"getback.sh\" unpacked with wrong size!
- fi
- chmod +x getback.sh
- # end of overwriting check
- fi
- if test -f ilw.c -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"ilw.c\"
- else
- echo shar: Extracting \"ilw.c\" \(3290 characters\)
- sed "s/^X//" >ilw.c <<'END_OF_ilw.c'
- X/*
- X * i-list-walker -- apply a specified function to all inodes on a filesystem
- X *
- X * Based on original routines by Larry Philps. This reimplementation is
- X * Copyright 1988 by Rayan Zachariassen, solely to prevent you from selling
- X * it or putting your name on it. Free (gratis) redistribution is encouraged.
- X */
- X
- X#include <stdio.h>
- X#include <sys/param.h>
- X#include <sys/types.h>
- X#include <sys/file.h>
- X#include <sys/time.h>
- X#include <sys/vnode.h>
- X#include <ufs/inode.h>
- X#include <ufs/fs.h>
- X
- Xextern char *progname; /* must be supplied by main program */
- X
- Xstatic char errmsgbuf[30];
- X#define EMSG(x) (errno < sys_nerr ? sys_errlist[x] : \
- X (sprintf(errmsgbuf, "unknown error %d", errno), errmsgbuf))
- X
- Xextern int errno, sys_nerr;
- Xextern char *sys_errlist[];
- Xextern off_t lseek();
- X
- Xint
- Xbread(fd, bno, buf, cnt)
- X daddr_t bno;
- X char *buf;
- X long cnt;
- X{
- X if (lseek(fd, (off_t) bno * DEV_BSIZE, L_SET) == -1) {
- X fprintf(stderr, "%s: bread: lseek: %s\n",
- X progname, EMSG(errno));
- X return -2;
- X }
- X if (read(fd, buf, (int) cnt) != cnt) {
- X fprintf(stderr, "%s: read error at block %u: %s\n",
- X progname, bno, EMSG(errno));
- X return -1;
- X }
- X return 0;
- X}
- X
- Xint
- Xbwrite(fd, bno, buf, cnt)
- X daddr_t bno;
- X char *buf;
- X long cnt;
- X{
- X if (lseek(fd, (off_t) bno * DEV_BSIZE, L_SET) == -1) {
- X fprintf(stderr, "%s: bwrite: lseek: %s\n",
- X progname, EMSG(errno));
- X return -2;
- X }
- X if (write(fd, buf, (int) cnt) != cnt) {
- X fprintf(stderr, "%s: write error at block %u: %s\n",
- X progname, bno, EMSG(errno));
- X return -1;
- X }
- X return 0;
- X}
- X
- X/*
- X * i-list-walker
- X *
- X * Opens the raw device indicated, and calls the function fn with an inode
- X * pointer for every inode on the device. If the function returns non-zero,
- X * the inode on the disk is updated, unless the readonly flag is set.
- X *
- X * Return -1 in case of error, +ve if inodes were updated, 0 otherwise.
- X */
- X
- Xilw(rawdev, fn, readonly)
- X char *rawdev;
- X int (*fn)();
- X int readonly;
- X{
- X register u_int ino;
- X register int fd, j, nfiles, changed, touched;
- X struct dinode *ip;
- X daddr_t iblk;
- X struct dinode itab[MAXBSIZE/sizeof(struct dinode)];
- X union {
- X struct fs u_sblock;
- X char dummy[SBSIZE];
- X } sb_un;
- X#define sblock sb_un.u_sblock
- X
- X if ((fd = open(rawdev, readonly ? O_RDONLY : O_RDWR, 0)) < 0) {
- X fprintf(stderr, "%s: open(%s): %s\n",
- X progname, rawdev, EMSG(errno));
- X return -1;
- X }
- X /*
- X * Don't bother stat'ing the device, we might want to do this on
- X * real files some day.
- X */
- X /* printf ("starting filesystem %s\n", rawdev); */
- X sync();
- X if (bread(fd, SBLOCK, (char *)&sblock, (long) SBSIZE) < 0) {
- X close(fd);
- X return -1;
- X }
- X nfiles = sblock.fs_ipg * sblock.fs_ncg;
- X touched = 0;
- X for (ino = 0; ino < nfiles; touched += changed) {
- X iblk = fsbtodb(&sblock, itod(&sblock, ino));
- X if (bread(fd, iblk, (char *)itab, sblock.fs_bsize) < 0) {
- X (void) close(fd);
- X return -1;
- X }
- X changed = 0;
- X for (j = 0; j < INOPB(&sblock) && ino < nfiles; j++, ino++) {
- X ip = &itab[j];
- X if ((ino < ROOTINO) || ((ip->di_mode&IFMT) == 0))
- X continue;
- X if ((*fn)(ip, ino))
- X ++changed;
- X }
- X if (!changed)
- X continue;
- X if (readonly)
- X printf("replacing block %d with %d changes\n",
- X iblk, changed);
- X else if (bwrite(fd, iblk, (char *)itab, sblock.fs_bsize) < 0) {
- X (void) close(fd);
- X return -1;
- X }
- X }
- X return touched;
- X}
- END_OF_ilw.c
- if test 3290 -ne `wc -c <ilw.c`; then
- echo shar: \"ilw.c\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- echo shar: End of shell archive.
- exit 0
-
-